﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using Newtonsoft.Json.Linq;

namespace youtubeFisher
{
	internal class YouTubeService
	{
		private string videoPageHtmlSource;

		private YouTubeService()
		{
			// Does nothing more than preventing the class from being instantiated.
		}

		private string videoUrl;
		public string VideoUrl
		{
			get { return videoUrl; }
		}

		private string videoTitle;
		public string VideoTitle
		{
			get { return videoTitle; }
		}

		private List<YouTubeVideoFile> availableVideoFormat = new List<YouTubeVideoFile>();
		public YouTubeVideoFile[] AvailableVideoFormat
		{
            /// Get (the format of) all videos available..
			get { return availableVideoFormat.ToArray(); }
		}

		public YouTubeVideoFile[] AvailableAudioFormat
		{
			get
			{
				return availableVideoFormat.FindAll(
					delegate(YouTubeVideoFile v) { return v.VideoFormat == YouTubeVideoType.Flash; }).ToArray();
			}
		}

		public static YouTubeService Create(string youTubeVideoUrl)
		{
			YouTubeService service = new YouTubeService();
			service.videoUrl = youTubeVideoUrl;

			service.GetVideoPageHtmlSource();
			service.GetVideoTitle();
			service.GetDownloadUrl();

			return service;
		}

		private void GetVideoPageHtmlSource()
		{
			HttpWebRequest req = HttpWebRequest.Create(videoUrl) as HttpWebRequest;
			HttpWebResponse resp = req.GetResponse() as HttpWebResponse;
			videoPageHtmlSource = new StreamReader(resp.GetResponseStream(), Encoding.UTF8).ReadToEnd();
			resp.Close();
		}

		private void GetVideoTitle()
		{
			try
			{
				string videoTitlePattern = @"\<meta name=""title"" content=""(?<title>.*)""\>";
				Regex videoTitleRegex = new Regex(videoTitlePattern, RegexOptions.IgnoreCase | RegexOptions.Compiled);
				Match videoTitleMatch = videoTitleRegex.Match(videoPageHtmlSource);

				if (videoTitleMatch.Success)
				{
					videoTitle = videoTitleMatch.Groups["title"].Value;
					videoTitle = System.Web.HttpUtility.HtmlDecode(videoTitle);

					// Remove the invalid characters in file names
					// In Windows they are: \ / : * ? " < > |
					videoTitle = Regex.Replace(videoTitle, @"[:\*\?""\<\>\|]", String.Empty);
					videoTitle = videoTitle.Replace("\\", "-").Replace("/", "-").Trim();
				}
			}
			catch (Exception)
			{
				videoTitle = "";
			}
		}

		private void GetDownloadUrl()
		{
			string availableFormats = "";
			string signature = "";
			string startConfig = "yt.playerConfig = ";
			int playerConfigIndex = videoPageHtmlSource.IndexOf(startConfig);

			if (playerConfigIndex > -1)
			{
				signature = videoPageHtmlSource.Substring(playerConfigIndex);
				int endOfJsonIndex = signature.IndexOf(");");
				signature = signature.Substring(startConfig.Length, endOfJsonIndex - 17).Trim();

				JObject playerConfig = JObject.Parse(signature);
				JObject playerArgs = JObject.Parse(playerConfig["args"].ToString());
				availableFormats = (string)playerArgs["url_encoded_fmt_stream_map"];

				string argument = "url=";
				string endOfQueryString = "&quality";

				if (availableFormats != "")
				{
					List<string> urlList = new List<string>(Regex.Split(availableFormats, argument));

					urlList.ForEach(delegate(string u)
					{
						if (!String.IsNullOrEmpty(u.Trim()))
						{
							Uri url = new Uri(Uri.UnescapeDataString(u.Substring(0, u.IndexOf(endOfQueryString))));
							NameValueCollection queryString = HttpUtility.ParseQueryString(url.Query);

							// for this version, only get the download URL
							byte formatCode = Byte.Parse(queryString["itag"]);
							// Currently based on youtube specifications (later we'll depend on the MIME type returned from the web request)
							availableVideoFormat.Add(new YouTubeVideoFile(url.ToString(), formatCode));
						}
					});
				}
			}
		}
	}
}
